﻿
namespace ZDS

open System
open System.Collections
open System.Collections.Generic

[<AllowNullLiteral>]
type ZBiDirectNode<'t>(value : 't)=
    let mutable m_value = value
    let mutable m_next : ZBiDirectNode<'t> = null
    let mutable m_prev : ZBiDirectNode<'t> = null

    member this.Value 
        with get() = m_value
        and set newvalue = m_value <- newvalue

    member this.Next 
        with get() = m_next
        and set newvalue = m_next <- newvalue

    member this.Prev
        with get() = m_prev
        and set newvalue = m_prev <- newvalue


type ZBiDirectLinkList<'t>()=
    let mutable m_size = 0
    let m_preHead = new ZBiDirectNode<'t>(Unchecked.defaultof<'t>)
    let mutable m_rear = m_preHead

    do
        m_preHead.Next <- m_rear
        m_rear.Prev <- m_preHead

    let nth_node n = 
        if n = -1 then
            m_preHead
        elif n = (m_size - 1) then
            m_rear
        else
            let mutable cursor  = m_preHead
            for index = 1 to (n+1) do
                cursor <- cursor.Next
            cursor

    member this.PreHead = m_preHead
    member this.Rear = m_rear
    member this.Size = m_size

    member this.Append value=
        let newnode = new ZBiDirectNode<'t>(value)

        m_rear.Next <- newnode
        newnode.Prev <- m_rear
        newnode.Next <- m_preHead
        m_preHead.Prev <- newnode

        m_rear <- newnode
        m_size <- m_size + 1

    member this.Item
        with get index = 
            if index >= 0 && index < m_size then
                let node = nth_node index
                node.Value
            else
                raise <| ZError(ErrorCode.OutofRange)
        and set index value = 
            if index >= 0 && index < m_size then
                let node = nth_node index
                node.Value <- value
            else
                raise <| ZError(ErrorCode.OutofRange)

    member this.Insert index value =
        if index >= 0 && index <= m_size then
            let prev = nth_node (index-1)
            let post = prev.Next

            let newnode = ZBiDirectNode<'t>(value)
            newnode.Next <- post
            prev.Next <- newnode
            newnode.Prev <- prev
            post.Prev <- newnode

            if index = m_size then
                m_rear <- newnode

            m_size <- m_size + 1
        else
            raise <| ZError(ErrorCode.OutofRange)

    member this.Remove index =
        if index >= 0 && index < m_size then
            let toremove = nth_node (index)

            toremove.Prev.Next <- toremove.Next
            toremove.Next.Prev <- toremove.Prev

            if index = (m_size - 1) then
                m_rear <- toremove.Prev
            m_size <- m_size - 1

            toremove.Value
        else
            raise <| ZError(ErrorCode.OutofRange)

    // ################### normal-order iterator
    interface IEnumerable<'t> with
        member this.GetEnumerator() =
            new ZBiLnkLstIterator<'t>(this,true) :> IEnumerator<'t>

    interface IEnumerable with
        member this.GetEnumerator() =
            new ZBiLnkLstIterator<'t>(this,true) :> IEnumerator

    // ################### reverse-order iterator
    member this.ReverseEnumerable =
        new ZReverseBiLnkLst<'t>(this)

and ZBiLnkLstIterator<'t>(m_list : ZBiDirectLinkList<'t>,isNormalOrder)=
    let mutable m_current = m_list.PreHead

    interface IDisposable with
        member this.Dispose() =
            ()

    interface IEnumerator<'t> with
        member this.Current =
            m_current.Value

    interface IEnumerator with
        member this.MoveNext() =
            if isNormalOrder then
                m_current <- m_current.Next
            else
                m_current <- m_current.Prev

            m_current <> m_list.PreHead

        member this.Reset() =
            m_current <- m_list.PreHead

        member this.Current =
            box m_current.Value

and ZReverseBiLnkLst<'t>(m_list : ZBiDirectLinkList<'t>) =

    interface IEnumerable<'t> with
        member this.GetEnumerator() =
            new ZBiLnkLstIterator<'t>(m_list,false) :> IEnumerator<'t>

    interface IEnumerable with
        member this.GetEnumerator() =
            new ZBiLnkLstIterator<'t>(m_list,false) :> IEnumerator
    

